#include"http_connect.h"
#include"epoll_add_del.h"

#define TEST

// 定义HTTP响应的一些状态信息
const char* ok_200_title = "OK";
const char* error_400_title = "Bad Request";
const char* error_400_form = "Your request has bad syntax or is inherently impossible to satisfy.\n";
const char* error_403_title = "Forbidden";
const char* error_403_form = "You do not have permission to get file from this server.\n";
const char* error_404_title = "Not Found";
const char* error_404_form = "The requested file was not found on this server.\n";
const char* error_500_title = "Internal Error";
const char* error_500_form = "There was an unusual problem serving the requested file.\n";

//网站的根目录  
const char* doc_root = "/home/cao/WebServer/resources";

int http_connect::m_epollfd = -1;
int http_connect::m_user_count = 0;

http_connect::http_connect(){
    
}
http_connect::~http_connect(){

}



void http_connect::init(int connfd, const sockaddr_in& client_address,util_timer* timer){
    this->m_sockfd = connfd;
    this->m_address = client_address;

    //设置端口复用
    int reuse = 1;
    setsockopt(this->m_sockfd,SOL_SOCKET,SO_REUSEPORT,&reuse,sizeof(reuse));

    //添加到epoll对象中
    addfd(m_epollfd,this->m_sockfd,true);
    m_user_count++; //总用户数加1

    http_time = timer;
    init();
}

void http_connect::init(){
     this->m_check_state = CHECK_STATE_REQUESTLINE; //初始化状态位为解析请求首行
     this->m_checked_index = 0;
     this->m_read_idx = 0;
     this->m_function = GET;
     this->m_url = NULL;
     this->m_vesion = NULL;
     this->m_host = NULL;
     this->m_linger = false;
     this->m_content_length = 0;
     this->m_file_address = NULL;
     this->m_write_index = 0;
     this->m_start_line = 0;

     bzero(m_read_buf,sizeof(m_read_buf));
     bzero(m_real_file,FILENAME_LEN);
     bzero(m_write_buf,sizeof(m_write_buf));

}

//关闭连接 
void http_connect::close_connection(){
    if(this->m_sockfd!=-1){
        removefd(m_epollfd,this->m_sockfd);
        this->m_sockfd = -1;
        m_user_count--;
    }
     

}
//解析HTTP请求
//主状态机，从大的范围解析请求
http_connect::HTTP_CODE http_connect::process_read(){
     
     LINE_STATUS line_status = LINE_OK;
     HTTP_CODE ret = NO_REQUEST;  //请求结果

     char* text;

     //解析每一行
     //读到了完整的一行
     while(((m_check_state==CHECK_STATE_CONTENT)&&(line_status==LINE_OK))||((line_status = parse_line())==LINE_OK)){
         //解析到了一行完整的数据，或者解析到了请求体，也是完成的数据
         //获取一行数据
        
         text = get_line();   //开始位置
         m_start_line = m_checked_index;
         cout<<"got one http line:"<<text<<endl;

         switch(m_check_state){
              case CHECK_STATE_REQUESTLINE:{
                  //解析请求行
                  ret = parse_request_line(text);
                  if(ret==BAD_REQUEST){
                      return BAD_REQUEST;
                  }
                  
                  break;
              }
              case CHECK_STATE_HEADER:{
                  //解析请求头
                 ret = parse_request_headers(text);
                 if(ret == BAD_REQUEST){
                     return BAD_REQUEST;
                     break;
                 }
                 else if(ret==GET_REQUEST){
                     //获得了一个完整的请求
                     return do_request(); //解析具体的内容
                 }
                 break;



              }
              case CHECK_STATE_CONTENT:{
                  //解析请求内容
                  ret = parse_request_content(text);
                  if(ret==GET_REQUEST){
                      return do_request();
                  }
                  line_status = LINE_OPEN;
                  break;

              }
              default:{
                  return INTERNAL_ERROR;
                  break;
              }
         }

        
     }
      
    return NO_REQUEST;
}

//解析HTTP请求行，获得请求方法，目标URL，HTTP版本
http_connect::HTTP_CODE http_connect::parse_request_line(char* text){
    //GET / HTTP/1.1
    this->m_url = strpbrk(text," \t"); 
    *(this->m_url)++ = '\0';

    char* method = text;
    if(strcasecmp(method,"GET")==0){
        m_function = GET;
    }
    else{
        return BAD_REQUEST;
    }

    //url: / HTTP/1.1
    this->m_vesion = strpbrk(m_url," \t");
    *(this->m_vesion)++ = '\0';
    if(strcasecmp(this->m_vesion,"HTTP/1.1")!=0){
        return BAD_REQUEST;
    }

    //解析初url
    //为了防止URL是http://192.168.110.129:10000/index.html格式的

    if(strncasecmp(m_url,"http://",7)==0){
        m_url+=7;
        //在参数str所指向的字符串中搜索第一次出现字符\的位置
        m_url= strchr(m_url,'/');
    }
    if(!m_url||m_url[0]!='/'){
        return BAD_REQUEST;
    }
    m_check_state = CHECK_STATE_HEADER;
    return NO_REQUEST;
}


http_connect::HTTP_CODE http_connect::parse_request_headers(char* text){
    //解析请求头

    //遇到空行请求完毕
    if(text[0]=='\0'){
        //如果HTTP还有请求体，则需要读取m_content_length字节的消息体
        //状态机转移到CHECK_STATE_CONTENT状态
        if(m_content_length!=0){
            m_check_state = CHECK_STATE_CONTENT;
            return NO_REQUEST;
        }
        else{
            return GET_REQUEST;
        }
    }
    else if(strncasecmp(text,"Connection:",11)==0){
        //处理Connection头部字段，Connection: Keep-alive
        text += 11;
        text += strspn(text," \t");
        if(strcasecmp(text,"keep-alive")==0){
            m_linger = true;
        }
    }
    else if(strncasecmp(text,"Content-Length:",15)==0){
        //处理Content-Length头部字段
        text+=15;
        text+=strspn(text," \t");
        m_content_length = atol(text);
    }
    else if(strncasecmp(text,"Host:",5)==0){
        //处理Host头部字段
        text+=5;
        text+= strspn(text," \t");
        m_host = text;
    }

    #ifdef TEST
    else{
    
        cout<<"This server parses only a few header files"<<endl;
    }
    #endif
    
    return NO_REQUEST;
}


//此处并未真正解析HTTP请求的消息体，只是判断它是否被完整的读入了
http_connect::HTTP_CODE http_connect::parse_request_content(char* text){
    //解析请求体
    if(m_read_idx>=(m_content_length+m_checked_index)){
        text[m_content_length]='\0';
        return GET_REQUEST; 
    }
    return NO_REQUEST;
}




//解析一行，判断依据\r\n;
http_connect::LINE_STATUS http_connect::parse_line(){
    char temp;
    for(;m_checked_index<m_read_idx;++m_checked_index){
        temp = m_read_buf[m_checked_index];
        if(temp=='\r'){
            //后面没数据了
            if((m_checked_index+1)==m_read_idx){
                return LINE_OPEN;
            } 
            else if(m_read_buf[m_checked_index+1] == '\n'){
                 m_read_buf[m_checked_index++] = '\0';
                 m_read_buf[m_checked_index++] = '\0';
                 return LINE_OK;
            }
            else{
                return LINE_BAD;
            }
        } 
        else if(temp=='\n'){
            /*
            老实说，我觉得这里执行不到，写了纯粹保险
            */
            if((m_checked_index>1)&&(m_read_buf[m_checked_index-1]=='\r')){
                m_read_buf[m_checked_index-1]=='\0';
                m_read_buf[m_checked_index++]== '\0';
                return LINE_OK;
            }
            return LINE_BAD;
        }
    }
    return LINE_OPEN;
}

http_connect::HTTP_CODE http_connect::do_request(){
    //在主状态机中解析完请求头或请求体后，得到了一个完整的http请求
    //test，请求的资源存放在 “/home/cao/WebServer/resources”
    strcpy(this->m_real_file,doc_root);
    int len = strlen(doc_root);
    //将资源存放根目录和请求文件组合起来
    //m_real_file中存放“/home/cao/WebServer/resources/index.html”
    strncpy(m_real_file+len,m_url,FILENAME_LEN-len-1);

    //获取请求文件的相关状态
    if(stat(m_real_file,&m_file_stat)==-1){
        return NO_RESOURCE;
    }

    //判断访问权限
    if(!(m_file_stat.st_mode&S_IROTH)){
        return FORBIDDEN_REQUEST;
    }

    //判断是否是目录
    if(S_ISDIR(m_file_stat.st_mode)){
        return BAD_REQUEST;
    }

    //是请求文件，将文件内容映射到内存中
    int fd = open(m_real_file,O_RDONLY);
    this->m_file_address = (char*)mmap(NULL,m_file_stat.st_size,PROT_READ,MAP_PRIVATE,fd,0);
    close(fd);
    return FILE_REQUEST;
}


//释放内存地址（内存映射）
void http_connect::unmap(){
    if(m_file_address!=NULL){
        munmap(this->m_file_address,m_file_stat.st_size);
        m_file_address = NULL;
    }
}


//生成响应信息
//根据服务器处理HTTP请求的结果，决定返回给客户端的内容
bool http_connect::process_write(HTTP_CODE read_ret){
    switch(read_ret){
        case INTERNAL_ERROR:{
            if(!add_status_line(500,"error_500_title")) //添加状态行
            {
                return false;
            }
            if(!add_headers(strlen(error_500_form)))  //添加状态头
            {
                return false;
            }
            if(!add_content(error_500_form)){  //添加内容
                return false;
            }
            
            break;
        }
        case BAD_REQUEST:{
            if(!add_status_line(400,"error_400_title")) //添加状态行
            {
                return false;
            }
            if(!add_headers(strlen(error_400_form)))  //添加状态头
            {
                return false;
            }
            if(!add_content(error_400_form)){  //添加内容
                return false;
            }
            break;
        }
        case NO_REQUEST:{
            if(!add_status_line(404,"error_404_title")) //添加状态行
            {
                return false;
            }
            if(!add_headers(strlen(error_404_form)))  //添加状态头
            {
                return false;
            }
            if(!add_content(error_404_form)){  //添加内容
                return false;
            }
            break;
        }
        case FORBIDDEN_REQUEST:{
            if(!add_status_line(403,"error_403_title")) //添加状态行
            {
                return false;
            }
            if(!add_headers(strlen(error_403_form)))  //添加状态头
            {
                return false;
            }
            if(!add_content(error_403_form)){  //添加内容
                return false;
            }
            break;
        }
        case FILE_REQUEST:{
            //确定请求的是一个文件
            if(!add_status_line(200,"ok_200_title")) //添加状态行
            {
                return false;
            }
            if(!add_headers(m_file_stat.st_size))  //添加状态头
            {
                return false;
            }
            //写入文件内容,此时写缓冲区可能太小，故不能简单处理
            //后期分开写入
            //先把要回送给客户端的写缓冲区地址存下来，在把要传送的具体内容 其映射到内存中的地址存下来
            m_iv[0].iov_base = m_write_buf;
            m_iv[0].iov_len = m_write_index;
            m_iv[1].iov_base = m_file_address;
            m_iv[1].iov_len = m_file_stat.st_size;
            m_iv_count = 2;
            return true;
        }
        default:{
            return false;
        }
    }
    m_iv[0].iov_base = m_write_buf;
    m_iv[0].iov_len = m_write_index;
    m_iv_count = 1;   //要写入的内存的个数

    return true;

}



bool http_connect::add_status_line(int status,const char* title){
    return add_response("%s %d %s\r\n","HTTP/1.1",status,title);
}

bool http_connect::add_headers(int content_length){
    bool statusOne = add_content_length(content_length);
    bool statusTwo = add_content_type();
    bool statusTree = add_linger();
    bool statusFour = add_blank_line();
    return statusOne&&statusTwo&&statusTree&&statusFour;
}

bool http_connect::add_content_length(int content_length){
    return add_response("Content-Length: %d\r\n",content_length);
}

bool http_connect::add_content_type(){
    return add_response("Content-Type: %s\r\n","text/html");
}

bool http_connect::add_linger(){
    return add_response("Connection: %s\r\n",(m_linger==true)?"keep-alive":"close");
}

bool http_connect::add_blank_line(){
    return add_response("%s","\r\n");
}

bool http_connect::add_content(const char* content){
    return add_response("%s",content);
}

bool http_connect::add_response(const char* format,...){
    if(m_write_index>=WRITE_BUFFER_SIZE){
        //写缓冲区写不下了
        return false;
    }
    va_list arg_list;
    va_start(arg_list,format); //使用format初始化arg_list
    int len = vsnprintf(m_write_buf+m_write_index,WRITE_BUFFER_SIZE-1-m_write_index,format,arg_list);
    if(len>=(WRITE_BUFFER_SIZE-1-m_write_index)){
        return false;
    }
    m_write_index+=len;
    va_end(arg_list);
    return true;
}




//主调用
//有线程池中的工作线程调用，这是处理HTTP请求的入口函数
void http_connect::process(){
    //解析http请求      
    HTTP_CODE read_ret = process_read();
    if(read_ret==NO_REQUEST){
        //请求不完整
        modfd(m_epollfd,m_sockfd,EPOLLIN);
        return;
    }

    //生成响应
    bool write_ret = process_write(read_ret);
    //采用的是proator模式，在主线程中全部读入
    if(!write_ret){
        close_connection();
    }
    else{
        modfd(m_epollfd,m_sockfd,EPOLLOUT);
    }

    

} 


bool http_connect::read(){
    //待写 
    if(this->m_read_idx>=READ_BUFFER_SIZE){
        return false;
    }

    //读取到的字节
    int bytes_read = 0;
    
    while(true){
        bytes_read = recv(this->m_sockfd,m_read_buf+this->m_read_idx,READ_BUFFER_SIZE-this->m_read_idx,0);
        if(bytes_read==-1){
            if(errno==EAGAIN||errno==EWOULDBLOCK){
                //没有数据
                break;
            }
            //移除定时器
            
            return false;
        }
        else if(bytes_read==0){
            //对方关闭连接
            return false;
        }
        m_read_idx += bytes_read;
    }
    cout<<"recvice chrome data:"<<m_read_buf<<endl;
    return true;
}
 
//已经监听到了写事件，写http响应
//状态行，状态头已经存放在m_write_buf里面了
bool http_connect::write(){
    int temp = 0;
    int bytes_have_send = 0; //已经发送的位置
    int bytes_to_send = m_write_index; //将要发送的字节数，即m_write_buf中待发送的字节数

    if(bytes_to_send==0){
        modfd(m_epollfd,m_sockfd,EPOLLIN);
        init();
        return true;
    }

    while(1){
        //分散写
        temp = writev(m_sockfd,m_iv,m_iv_count);
        if(temp==-1){
            //当前TCP写缓冲中没有空间，则等待下一轮EPOLLOUT事件，继续保持链接（但此时无法接到同一个客户的下一个请求，因为）
            if(temp==EAGAIN){
                modfd(m_epollfd,m_sockfd,EPOLLIN);
                return true;
            }
        }
        bytes_have_send += temp;
        bytes_to_send -= temp;
        if(bytes_to_send<=bytes_have_send){
            //发送成果，且根据HTTP请求中的Connection字段决定是否立即关闭连接
            unmap();
            if(m_linger){
                init(); //继续监听
                modfd(m_epollfd,m_sockfd,EPOLLIN);
                return true;
            }
            else{
                // modfd(m_epollfd,m_sockfd,EPOLLIN);
                return false; //不监听了，由主线程关闭
            }
        }
    }
}